package mongo

import (
	"context"
	"go2/log"
	"sync"
	"time"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var (
	mMutex     sync.RWMutex
	mpMGClient *mongo.Client
)

// MongoDB配置选项
type Config struct {
	MaxRetries       int
	RetryInterval    time.Duration
	ConnectTimeout   time.Duration
	PingTimeout      time.Duration
	SocketTimeout    time.Duration
	ServerSelectionTimeout time.Duration
	EnableLog        bool
	EnablePool       bool
	MaxPoolSize      uint64
	MinPoolSize      uint64
	MaxConnIdleTime  time.Duration
	MaxConnecting    uint32
}

// 默认配置
var DefaultConfig = Config{
	MaxRetries:           5,
	RetryInterval:        2 * time.Second,
	ConnectTimeout:       10 * time.Second,
	PingTimeout:          5 * time.Second,
	SocketTimeout:        30 * time.Second,
	ServerSelectionTimeout: 30 * time.Second,
	EnableLog:            true,
	EnablePool:           true,
	MaxPoolSize:          100,
	MinPoolSize:          5,
	MaxConnIdleTime:      30 * time.Minute,
	MaxConnecting:        10,
}

// 连接状态
type ConnectionStatus struct {
	Connected bool
	Error     error
	Server    string
}

func EndWorking() {
	Close()
}

// 优化的Init函数
func Init(max int, opts ...*options.ClientOptions) {
	InitWithConfig(opts, DefaultConfig)
}

// 带配置的初始化函数
func InitWithConfig(opts []*options.ClientOptions, config Config) {
	if len(opts) == 0 {
		log.Error("MongoDB配置选项不能为空")
		return
	}

	mMutex.Lock()
	defer mMutex.Unlock()

	if mpMGClient != nil {
		return
	}

	if config.EnableLog {
		log.Debug("正在连接MongoDB...")
	}

	// 应用配置到选项
	optimizedOpts := applyConfigToOptions(opts[0], config)

	// 使用重试机制连接
	client, err := connectWithRetry(optimizedOpts, config)
	if err != nil {
		log.Errorf("MongoDB连接失败: %v", err)
		return
	}

	mpMGClient = client
	if config.EnableLog {
		log.Debug("MongoDB连接成功")
	}
}

// 应用配置到MongoDB选项
func applyConfigToOptions(opt *options.ClientOptions, config Config) *options.ClientOptions {
	// 创建新选项以避免修改原选项
	newOpt := opt

	if config.ConnectTimeout > 0 {
		newOpt.SetConnectTimeout(config.ConnectTimeout)
	}

	if config.SocketTimeout > 0 {
		newOpt.SetSocketTimeout(config.SocketTimeout)
	}

	if config.ServerSelectionTimeout > 0 {
		newOpt.SetServerSelectionTimeout(config.ServerSelectionTimeout)
	}

	// 连接池配置
	if config.EnablePool {
		if config.MaxPoolSize > 0 {
			newOpt.SetMaxPoolSize(config.MaxPoolSize)
		}
		if config.MinPoolSize > 0 {
			newOpt.SetMinPoolSize(config.MinPoolSize)
		}
		if config.MaxConnIdleTime > 0 {
			newOpt.SetMaxConnIdleTime(config.MaxConnIdleTime)
		}
		if config.MaxConnecting > 0 {
			newOpt.SetMaxConnecting(uint64(config.MaxConnecting))
		}
	}

	return newOpt
}

// 带重试的连接策略
func connectWithRetry(opt *options.ClientOptions, config Config) (*mongo.Client, error) {
	var lastErr error

	for i := 0; i < config.MaxRetries; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), config.ConnectTimeout)

		client, err := mongo.Connect(ctx, opt)
		cancel()

		if err != nil {
			lastErr = err
			if config.EnableLog {
				log.Errorf("MongoDB第%d次连接失败: %v", i+1, err)
			}
		} else {
			// 测试连接
			if err := pingWithRetry(client, config); err == nil {
				return client, nil
			}
			lastErr = err

			// 关闭失败的连接
			ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
			client.Disconnect(ctx)
			cancel()
		}

		if i < config.MaxRetries-1 {
			time.Sleep(config.RetryInterval)
		}
	}

	return nil, lastErr
}

// 带重试的Ping测试
func pingWithRetry(client *mongo.Client, config Config) error {
	ctx, cancel := context.WithTimeout(context.Background(), config.PingTimeout)
	defer cancel()

	for i := 0; i < config.MaxRetries; i++ {
		if err := client.Ping(ctx, nil); err == nil {
			return nil
		} else if config.EnableLog {
			log.Errorf("MongoDB第%d次Ping测试失败: %v", i+1, err)
		}

		if i < config.MaxRetries-1 {
			time.Sleep(config.RetryInterval)
		}
	}

	return ctx.Err()
}

// 线程安全的GetMGClient
func GetMGClient() *mongo.Client {
	mMutex.RLock()
	defer mMutex.RUnlock()
	return mpMGClient
}

// 获取连接状态
func GetStatus() ConnectionStatus {
	mMutex.RLock()
	defer mMutex.RUnlock()

	if mpMGClient == nil {
		return ConnectionStatus{
			Connected: false,
			Error:     mongo.ErrNoDocuments,
			Server:    "",
		}
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	err := mpMGClient.Ping(ctx, nil)
	if err != nil {
		return ConnectionStatus{
			Connected: false,
			Error:     err,
			Server:    "",
		}
	}

	// 获取服务器信息
	serverInfo := mpMGClient.Database("admin").RunCommand(ctx, map[string]interface{}{"serverStatus": 1})
	var result struct {
		Host string `bson:"host"`
	}
	if err := serverInfo.Decode(&result); err == nil {
		return ConnectionStatus{
			Connected: true,
			Error:     nil,
			Server:    result.Host,
		}
	}

	return ConnectionStatus{
		Connected: true,
		Error:     nil,
		Server:    "Unknown",
	}
}

// 关闭连接
func Close() error {
	mMutex.Lock()
	defer mMutex.Unlock()

	if mpMGClient == nil {
		return nil
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	err := mpMGClient.Disconnect(ctx)
	if err != nil {
		log.Errorf("关闭MongoDB连接失败: %v", err)
		return err
	}

	mpMGClient = nil
	if DefaultConfig.EnableLog {
		log.Debug("MongoDB连接已关闭")
	}
	return nil
}

// 重置连接
func Reset(config Config, opts ...*options.ClientOptions) error {
	if err := Close(); err != nil {
		return err
	}

	if len(opts) > 0 {
		InitWithConfig(opts, config)
	} else {
		log.Error("重置连接需要提供配置选项")
	}
	return nil
}
